package com.jintian.smart.kernel.orm.function;

import java.lang.reflect.Field;
import java.util.List;

import com.jintian.smart.kernel.common.util.ObjectUtil;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.annotation.orm.Condition;
import com.jintian.smart.kernel.core.orm.ISimpleCondition;
import com.jintian.smart.kernel.core.annotation.beetl.BeetlFunction;
import com.jintian.smart.kernel.core.domain.PageQuery;
import org.beetl.core.Context;
import org.beetl.core.Function;
import org.beetl.sql.core.ExecuteContext;
import org.beetl.sql.core.engine.SQLParameter;
import org.beetl.sql.core.query.util.FieldsUtil;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.ReflectionUtils.FieldFilter;

/**
 * SQLPlaceholderST.textFunList需要添加，否则将作为参数处理
 */
@BeetlFunction(placeHolder = true, code = "simpleQuery")
public class SimpleQueryFunction implements Function {

    private static final String AND = " and ";
    private static final int AND_LENGTH = AND.length();
    private static final String EQT = " = ?";
    private static final String GT_ET = " >= ?";
    private static final String LT_ET = " <= ?";
    private static final String BWT = " between ? and ?";
    private static final String LIKE = " like '%' || ? || '%'";

    @SuppressWarnings({"unchecked"})
    @Override
    public Object call(Object[] paras, Context ctx) {
        List<Object> _paras = (List<Object>) ctx.getGlobal("_paras");
        Object param = ctx.globalVar.get(ExecuteContext.ROOT_PARAM);
        if (param == null) {
            for (Object obj : ctx.globalVar.values()) {
                if (obj instanceof ISimpleCondition) {
                    param = obj;
                    break;
                } else if (obj instanceof PageQuery) {
                    param = obj;
                    break;
                }
            }
        }
        if (param instanceof PageQuery) {
            param = ((PageQuery<?, ?>) param).getSimpleCondition();
        }
        if (!(param instanceof ISimpleCondition)) {
            return "";
        }
        ISimpleCondition ISimpleCondition = (ISimpleCondition) param;
        ExecuteContext executeContext = (ExecuteContext) ctx.getGlobal(ExecuteContext.NAME);
        StringBuilder sb = new StringBuilder();
        org.springframework.util.ReflectionUtils.doWithFields(param.getClass(), new FieldCallback() {

            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                field.setAccessible(true);
                Condition condition = field.getAnnotation(Condition.class);
                Object value = field.get(ISimpleCondition);
                if (value == null) {
                    return;
                }
                String talbeField = condition.column();
                if (StringUtil.isEmpty(talbeField)) {
                    talbeField = executeContext.target != null
                            ? FieldsUtil.getColName(executeContext.sqlManager, executeContext.target, field.getName())
                            : field.getName();
                }
                if (StringUtil.isNotEmpty(condition.table())) {
                    talbeField = condition.table() + "." + talbeField;
                }
                if (StringUtil.isNoneBlank(condition.custom())) {
                    if (ObjectUtil.isNotEmpty(value)) {
                        int cnt = getQuestionMarkCount(condition.custom());
                        while (cnt-- > 0) {
                            SQLParameter sqlPara = new SQLParameter(field.getName(), value);
                            _paras.add(sqlPara);
                        }
                        sb.append(AND).append("(").append(condition.custom()).append(")");
                    }
                } else if (condition.fuzzy()) {
                    if (field.getType() == String.class) {
                        field.setAccessible(true);
                        if (!value.toString().isEmpty()) {
                            SQLParameter sqlPara = new SQLParameter(field.getName(), value);
                            _paras.add(sqlPara);
                            sb.append(AND).append(talbeField).append(LIKE);
                        }
                    }
                } else if (condition.type() == Condition.TYPE_GENERAL) {
                    SQLParameter sqlPara = new SQLParameter(field.getName(), value);
                    _paras.add(sqlPara);
                    sb.append(AND).append(talbeField).append(EQT);
                } else if (condition.type() == Condition.TYPE_VALUE_BETWEEN) {
                    String name = field.getName();
                    Object minValue = null, maxValue = null;
                    {
                        String minName = name + "Min";
                        Field minField = ReflectionUtils.findField(ISimpleCondition.getClass(), minName);
                        if (minField != null) {
                            minField.setAccessible(true);
                            minValue = ReflectionUtils.getField(minField, ISimpleCondition);
                        }
                    }

                    {
                        String maxName = name + "Max";
                        Field maxField = ReflectionUtils.findField(ISimpleCondition.getClass(), maxName);
                        if (maxField != null) {
                            maxField.setAccessible(true);
                            maxValue = ReflectionUtils.getField(maxField, ISimpleCondition);
                        }
                    }

                    if (minValue != null && maxValue == null) {
                        SQLParameter sqlPara = new SQLParameter(field.getName(), minValue);
                        _paras.add(sqlPara);
                        sb.append(AND).append(talbeField).append(GT_ET);
                    } else if (minValue == null && maxValue != null) {
                        SQLParameter sqlPara = new SQLParameter(field.getName(), maxValue);
                        _paras.add(sqlPara);
                        sb.append(AND).append(talbeField).append(LT_ET);
                    } else if (minValue != null && maxValue != null) {
                        SQLParameter sqlPara = new SQLParameter(field.getName(), minValue);
                        _paras.add(sqlPara);
                        sqlPara = new SQLParameter(field.getName(), maxValue);
                        _paras.add(sqlPara);
                        sb.append(AND).append(talbeField).append(BWT);
                    }
                }
            }
        }, new FieldFilter() {
            @Override
            public boolean matches(Field field) {
                return field.getAnnotation(Condition.class) != null;
            }
        });
        if (sb.length() > 0) {
            return sb.substring(AND_LENGTH);
        }
        return "";
    }

    private int getQuestionMarkCount(String condition) {
        int s = 0;
        int cnt = 0;
        int idx = -1;
        while ((idx = condition.indexOf("?", s)) >= 0) {
            cnt++;
            s = idx + 1;
        }
        return cnt;
    }
}
